#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/core/make_raw_handle.h>
#include <vex/core/make_intrusive_handle.h>
#include <vex/core/make_unique_handle.h>

#include <vex/iterable/range_base.h>
#include "range_archetype.h"
#include "forward_range_call.h"
#include <vex/iterable/implementation/range_default_impl.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_default_impl {

    namespace vc = vex::core;
    namespace vcc = vex::core::contract;
    namespace vi = vex::iterable;
    namespace vic = vex::iterable::contract;
    namespace vri = vex::iterable::implementation;

    TEST_CLASS(range_default_impl)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.range_default_impl")
            END_TEST_CLASS_ATTRIBUTE();
    public:
        template<class TRangeTraits>
        void raw_range_type_and_size_are_as_expected_impl()
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef TRangeTraits traits;
            typedef typename traits::range_type iterable;
            typedef typename traits::category category;
            typedef typename traits::reference reference;
            typedef vic::range<category, reference> range_contract;
            typedef vri::range_default_impl<iterable> range_impl;

            typedef decltype(vc::make_raw_handle<range_impl>(std::declval<iterable>())) range_handle_ptr;
            typedef std::remove_pointer<range_handle_ptr>::type range_handle;

            Assert::IsTrue(std::is_base_of<range_contract, range_handle>::value);
            Assert::IsTrue(std::is_base_of<range_contract, range_impl>::value);
            Assert::IsTrue(std::is_base_of<range_impl, range_handle>::value);
            Assert::IsTrue(std::is_convertible<range_handle_ptr, range_impl*>::value);
            Assert::IsTrue(std::is_convertible<range_handle_ptr, range_contract*>::value);
            Assert::IsTrue(std::is_convertible<range_impl*, range_contract*>::value);
        }

        template<class TRangeTraits>
        void range_is_compatible_to_all_supertypes_impl(int p_exp_client_called)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            typedef TRangeTraits traits;
            typedef typename traits::range_type iterable;
            typedef typename traits::category category;
            typedef typename traits::reference reference;
            typedef vic::range<category, reference> range_contract;
            typedef vri::range_default_impl<iterable> range_impl;

            if (auto t_handle = vc::make_raw_handle<range_impl>(iterable())) {
                Assert::AreEqual(p_exp_client_called, forward_range_call().call(t_handle));
                t_handle->release();
            }
        }

        TEST_METHOD(trivial_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(one_pass_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<vi::one_pass_range_tag, int&> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(output_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<vi::output_range_tag, int> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(forward_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<vi::forward_range_tag, int&> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(bidirectional_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<vi::bidirectional_range_tag, int&> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(random_access_raw_range_type_and_size_are_as_expected)
        {
            typedef vi::range_traits< range_archetype<vi::random_access_range_tag, int&> > traits;
            this->raw_range_type_and_size_are_as_expected_impl<traits>();
        }

        TEST_METHOD(trivial_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<> > traits;
            this->range_is_compatible_to_all_supertypes_impl<traits>(1);
        }

        TEST_METHOD(one_pass_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<vi::one_pass_range_tag, int&> > traits;
            this->range_is_compatible_to_all_supertypes_impl<traits>(2);
        }

        TEST_METHOD(output_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<vi::output_range_tag, int> > traits;
            Assert::IsTrue(std::is_same<int, traits::value_type>::value);
            this->range_is_compatible_to_all_supertypes_impl<traits>(2);
        }

        TEST_METHOD(forward_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<vi::forward_range_tag, int&> > traits;
            Assert::IsTrue(std::is_same<int, traits::value_type>::value);
            this->range_is_compatible_to_all_supertypes_impl<traits>(3);
        }

        TEST_METHOD(bidirectional_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<vi::bidirectional_range_tag, int&> > traits;
            Assert::IsTrue(std::is_same<int, traits::value_type>::value);
            this->range_is_compatible_to_all_supertypes_impl<traits>(4);
        }

        TEST_METHOD(random_access_raw_range_is_compatible_to_all_supertypes)
        {
            typedef vi::range_traits< range_archetype<vi::random_access_range_tag, int&> > traits;
            Assert::IsTrue(std::is_same<int, traits::value_type>::value);
            this->range_is_compatible_to_all_supertypes_impl<traits>(5);
        }
    };
}}